#include "Jeu.h"
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <math.h>


/* Procédure qui va permettre l'initialisation de la structure jeu.
 * Elle prend en paramètre un pointeur sur la structure jeu.
 * Cette procédure créé en fait 6 pointeurs de la structure mais elle initialise aussi les structures associées à
 * chacun de ces pointeurs.*/
void init_jeu (Sjeu* jeu)
{
	// Affectation du pointeur sur le terrain et initialisation de la structure terrain associée grace à la procédure
	// prévue à cet effet.
	(*jeu).terrain = new Sterrain;
	init_terrain ((*jeu).terrain);
	// Affectation du pointeur sur la liste des types et initialisation de la structure liste de types associée grace
	// à la procédure prévue à cet effet.
	(*jeu).types = new Stype;
	init_listetype ((*jeu).types);
	// Affectation du pointeur sur la liste des tours et initialisation de la structure liste de tours associée grace
	// à la procédure prévue à cet effet.
	(*jeu).tours = new Slistetour;
	init_listetour((*jeu).tours);
	// Affectation du pointeur sur la liste des bebetes et initialisation de la structure liste de bebetes associée grace
	// à la procédure prévue à cet effet.
	(*jeu).bebetes = new Slistebebete;
	init_listebebete ((*jeu).bebetes);
	// Affectation du pointeur sur la partie et initialisation de la structure partie associée grace à la procédure
	// prévue à cet effet.
	(*jeu).partie = new Spartie;
	init_partie ((*jeu).partie);
	// Affectation du pointeur sur le menu et initialisation de la structure menu associée grace à la procédure
	// prévue à cet effet.
	(*jeu).menu = new Smenu;
	init_menu ((*jeu).menu);
}

/* Cette fonction est un accesseur au pointeur sur terrain :
 * AUCUN DE SES PARAMETRES NE POURRA ETRE MODIFIE APRES SON EXECUTION.
 * Elle prend en paramètre un pointeur sur une structure jeu.
 * Elle renvoie un pointeur sur une structure terrain */
Sterrain* get_jeuterrain (const Sjeu* jeu)
{
	return ((*jeu).terrain);
}

/* Cette fonction est un accesseur au pointeur sur la liste de types :
 * AUCUN DE SES PARAMETRES NE POURRA ETRE MODIFIE APRES SON EXECUTION.
 * Elle prend en paramètre un pointeur sur une structure jeu.
 * Elle renvoie un pointeur sur une structure liste de type */
Stype* get_jeutypetour (const Sjeu* jeu)
{
	return ((*jeu).types);
}

/* Cette fonction est un accesseur au pointeur sur la liste de tours :
 * AUCUN DE SES PARAMETRES NE POURRA ETRE MODIFIE APRES SON EXECUTION.
 * Elle prend en paramètre un pointeur sur une structure jeu.
 * Elle renvoie un pointeur sur une structure liste de tours */
Slistetour* get_jeulistetour (const Sjeu* jeu)
{
	return ((*jeu).tours);
}

/* Cette fonction est un accesseur au pointeur sur la liste de bebetes :
 * AUCUN DE SES PARAMETRES NE POURRA ETRE MODIFIE APRES SON EXECUTION.
 * Elle prend en paramètre un pointeur sur une structure jeu.
 * Elle renvoie un pointeur sur une structure liste de bebetes */
Slistebebete* get_jeulistebebete (const Sjeu* jeu)
{
	return ((*jeu).bebetes);
}

/* Cette fonction est un accesseur au pointeur sur la partie :
 * AUCUN DE SES PARAMETRES NE POURRA ETRE MODIFIE APRES SON EXECUTION.
 * Elle prend en paramètre un pointeur sur une structure jeu.
 * Elle renvoie un pointeur sur une structure partie */
Spartie* get_jeupartie (const Sjeu* jeu)
{
	return ((*jeu).partie);
}

/* Cette fonction est un accesseur au pointeur sur menu :
 * AUCUN DE SES PARAMETRES NE POURRA ETRE MODIFIE APRES SON EXECUTION.
 * Elle prend en paramètre un pointeur sur une structure jeu.
 * Elle renvoie un pointeur sur une structure menu */
Smenu* get_jeumenu (const Sjeu* jeu)
{
	return ((*jeu).menu);
}

/* Cette fonction permet de savoir si on arrive à la fin du niveau.
 * AUCUN DE SES PARAMETRES NE POURRA ETRE MODIFIE APRES SON EXECUTION.
 * Elle prend en paramètre un pointeur sur une structure jeu.
 * Elle renvoie en fait un booléen : 0 si on n'est pas à la fin du niveau et 1 si on est à la fin du niveau. */
int fin_niveau (const Sjeu* jeu)
{
	// S'il n'y a plus de bebetes dans la liste des bebetes c'est qu'on les a toutes tuées et que le niveau est terminé
	// donc si la liste de bebetes est nulle, on renvoie 1
	if (((*jeu).bebetes==NULL)||((*(*jeu).bebetes).pbebete==NULL))
	{
		return (1);
	}
	// sinon on renvoie 0.
	else
	{
		return (0);
	}
}

/* Cette fonction permet de savoir si on arrive à la fin de la partie.
 * AUCUN DE SES PARAMETRES NE POURRA ETRE MODIFIE APRES SON EXECUTION.
 * Elle prend en paramètre un pointeur sur une structure jeu.
 * Elle renvoie en fait un booléen : 0 si on n'est pas à la fin de la partie et 1 si on est à la fin de la partie. */
int fin_partie (const Sjeu* jeu)
{
	// On créé un pointeur temporaire sur une partie puis on utilise l'accesseur afin d'y mettre la valeur du pointeur
	// sur partie (on aurait aussi pu faire (*Sjeu).partie mais l'accesseur n'a alors plus d'interet !)
	Spartie* partietemp;
	partietemp = get_jeupartie (jeu);
	// Si le champs niveau vaut 10 alors c'est la fin de la partie (car il n'y a en fait que 9 niveau, si on est passé au
	// 10eme, c'est qu'on a incrémenté la victoire !) ou si on a perdu, on renvoie 1.
	if (((*partietemp).niveau==10)||(perdu(jeu)==1))
	{
		return (1);
	}
	// sinon on renvoie 0.
	else
	{
		return (0);
	}
}


/* Cette fonction permet de savoir si on a perdu la partie.
 * AUCUN DE SES PARAMETRES NE POURRA ETRE MODIFIE APRES SON EXECUTION.
 * Elle prend en paramètre un pointeur sur une structure jeu.
 * Elle renvoie en fait un booléen : 0 si on n'a pas perdu et 1 si on a perdu la partie. */
int perdu (const Sjeu* jeu)
{
	// si on n'a plus de vie, on a perdu
	if (get_nbvie((*jeu).partie)<=0)
	{
		return(1);
	}
	// sinon tout va bien !
	else
	{
		return(0);
	}
}

/* Cette procédure va permettre de faire tirer l'ensemble des tours au mommet où
 * elle sera appellée. (Elle a un fonctionnement itératif et non récursif).
 * Elle prend en paramètre un pointeur sur une structure jeu. */
void tir_tour (Sjeu* jeu)
{
	// on commence par déclarer les variables :
	Slistebebete* beb; // va permettre de se déplacer dans la liste de bebete
	Slistetour* listetour; // idem pour la liste de tour
	int portee;
	int fini = 0; // booléen qui permettra de gere l'arret de la boucle
	time_t temps; // va permettre de gérer le temps entre chaque tir
	// s'il n'y a pas de tour, on a fini :
	if (((*jeu).tours==NULL)||((*(*jeu).tours).ptour==NULL))
	{
		return;
	}
	beb = (*jeu).bebetes; // on met la première bebete dans le pointeur courant
	listetour = (*jeu).tours; // ainsi que la première tour
	temps = time(NULL); // on récupère un temps de référence
	
	// tant qu'il reste des tours ou des bebetes,
	while ((listetour!=NULL)&&((*listetour).ptour!=NULL)&&(beb!=NULL)&&((*beb).pbebete!=NULL))
	{
		// on stocke la portée de la tour courante
		portee = get_ptypeportee((*listetour).ptour);
		// si le temps depuis le dernier tir est suffisant par rapport à la vitesse de la tour
		if ((temps-((*(*listetour).ptour).derniertir))>4/(get_ptypevitesse((*listetour).ptour)))
		{
			do
			{
				//s'il reste des bebetes dans la liste apres
				if ((*beb).pbebetesuiv!=NULL)
				{
					// et si la portée de la tour n'est pas suffisante
					if (((fabs(get_Xbebete((*beb).pbebete)-get_Xtour((*listetour).ptour)))>portee)||((fabs(get_Ybebete((*beb).pbebete)-get_Ytour((*listetour).ptour)))>portee))
					{
						// alors on passe à la bebete suivante.
						beb = (*beb).pbebetesuiv;
					}
					// sinon si la portée est suffisante, on tir et on met à jour le dernier tir de la tour
					else
					{
						//printf ("La tour %d a tiré a %ld \n", i, temps);
						bebete_touchee((*listetour).ptour,(*jeu).bebetes, jeu);
						set_dernier_tir((*listetour).ptour, temps);
						// si c'est la fin du niveau, on arrete la boucle, sinon on continue :
						if (fin_niveau(jeu)==1)
						{
							return;
						}
						fini = 1; // on précise que la tour à fini puisqu'elle vient de tirer
					}
				}
				// si on en est à la dernière bebete et que la portee est suffisante, on tir et on met a jour le dernier tir.
				else if (((fabs(get_Xbebete((*beb).pbebete)-(get_Xtour((*listetour).ptour))))<=portee)&&((fabs(get_Ybebete((*beb).pbebete)-get_Ytour((*listetour).ptour)))<=portee))
				{
					//printf ("La tour %d a tiré a %ld \n", i, temps);
					bebete_touchee((*listetour).ptour, (*jeu).bebetes, jeu);
					set_dernier_tir((*listetour).ptour, temps);
					if (fin_niveau(jeu)==1)
					{
						return;
					}
					fini = 1;
				}
				// sinon, s'il n'y a plus de bebetes à viser, on a fini avec cette tour
				else
				{
					fini = 1;
				}
			}
			while (fini != 1);
		}
		// on incrémente la tour
		listetour = (*listetour).ptoursuiv;
	}
}

/* Cette procédure permet de décrémenter le niveau de vie d'une bebete
 * et de mettre à jour les données argent et score. Elle n'est appellée que lorsqu'une
 * bebete est touchée par une tour.
 * Elle prend en parametre un pointeur sur une structure jeu. */
void bebete_touchee (Stour* tour, Slistebebete*& listebeb, Sjeu* jeu)
{
	// On met à jour le niveau de vie de la bebete touchée selon la puissance de la tour
	set_niveauvie ((*listebeb).pbebete, (get_niveauvie((*listebeb).pbebete)-get_ptypepuissance(tour)));
	// si le niveau de vie est inférieur à 0, la bebete est morte
	if (get_niveauvie((*listebeb).pbebete)<=0)
	{
		// donc on la supprime de la liste et on ajoute l'argent et les points correspondants :
		supbebete(listebeb, (*listebeb).pbebete);
		set_argent((*jeu).partie,-1);
		set_score((*jeu).partie,10);
	}
}



/* Procédure de gestion des évenements souris.
 * SEULE LA STRUCTURE POURRA ETRE MODIFIEE APRES EXECUTION DE CETTE PROCEDURE.
 * Cette procédure prend en paramètres un pointeur sur la structure jeu, la valeur du clic souris, et un X et un Y
 * correspondants au coordonnées de cliquage.
 * Elle va en fait déclancher les actions à réaliser sur les différentes structures selon les clics du joueurs. */
void actionsouris (Sjeu* jeu, const char clic, const int X, const int Y)
{
	// on aura besoin de modifier la liste de tour et donc de connaitre le type de la tour et du coup son prix (afin de
	// pouvoir également modifier le montant de l'argent restant en banque).
	// On créé donc les variables locales correspondantes :
	int prix;
	Stypetour* typetemp;
	switch(clic)
	{
	// Pour les 4 premiers cas, on traite en fait le momment où le joueur "plante" une tour :
		case '1' : 
				// récupération de l'adresse du type 10
				typetemp = get_listetype ((*jeu).types, 10);
				// récupération du prix de la tour 10
				prix = get_prix (typetemp);
				if (get_argent((*jeu).partie)>=prix)
				{
					// d'abord ajout de la tour à la liste de tour
					ajouttour ((*jeu).tours, X, Y, 10, (*jeu).terrain, (*jeu).types);
					// Modification de reste de sous selon le prix récupéré
					set_argent ((*jeu).partie, prix);
				}
				break;
		case '2' :
				typetemp = get_listetype ((*jeu).types, 20);
				prix = get_prix (typetemp);
				if (get_argent((*jeu).partie)>=prix)
				{
					ajouttour ((*jeu).tours, X, Y, 20, (*jeu).terrain, (*jeu).types);
					set_argent ((*jeu).partie, prix);
				}
				break;
		case '3' :
				typetemp = get_listetype ((*jeu).types, 30);
				prix = get_prix (typetemp);
				if (get_argent((*jeu).partie)>=prix)
				{
					ajouttour ((*jeu).tours, X, Y, 30, (*jeu).terrain, (*jeu).types);
					set_argent ((*jeu).partie, prix);
				}
				break;
		case '4' : 
				typetemp = get_listetype ((*jeu).types, 40);
				prix = get_prix (typetemp);
				if (get_argent((*jeu).partie)>=prix)
				{
					ajouttour ((*jeu).tours, X, Y, 40, (*jeu).terrain, (*jeu).types);
					set_argent ((*jeu).partie, prix);
				}
				break;
	// On traite ici le cas d'une suppression de tour
		case 's' :
				suptour ((*jeu).tours, X, Y, (*jeu).terrain);
	// On traite ici l'upgrade d'une tour
		case 'u' :
				upgrade(get_listetour ((*jeu).tours, X, Y));
				break;
	}
}

